part of internal;

class Selection {
   Position _anch;
   Position _head;
   int _cc;
   DoublyLinkedList<Line> _rend;

   // generator
   Selection (DoublyLinkedList<Line> rend) {
      _rend = rend;
   }

   // internal
   void _hide () {
      if (_cc > 0) for (int i = _anch.ln; i <= _head.ln; i++) _rend.nth(i).selOff();
      if (_cc < 0) for (int i = _head.ln; i <= _anch.ln; i++) _rend.nth(i).selOff();
   }
   void _show (int width) {
      if (_cc > 0) {
         for (int i = _anch.ln; i <= _head.ln; i++) {
            if (i == _anch.ln && i == _head.ln) _rend.nth(i).selOn(_anch.ch, _head.ch);
            else if (i == _anch.ln) _rend.nth(i).selOn(_anch.ch, width);
            else if (i == _head.ln) _rend.nth(i).selOn(0, _head.ch);
            else _rend.nth(i).selOn(0, width);
         }
      }
      if (_cc < 0) {
         for (int i = _head.ln; i <= _anch.ln; i++) {
            if (i == _head.ln && i == _anch.ln) _rend.nth(i).selOn(_head.ch, _anch.ch);
            else if (i == _head.ln) _rend.nth(i).selOn(_head.ch, width);
            else if (i == _anch.ln) _rend.nth(i).selOn(0, _anch.ch);
            else _rend.nth(i).selOn(0, width);
         }
      }
   }

   // mutators
   void init (Buffer buf, int width, Position anch, Position head) {
      if (dev) {
         if (anch.ln >= buf.lc) argErr(anch);
         if (anch.ch > buf.nth(anch.ln).length) argErr(anch);
         if (head.ln >= buf.lc) argErr(head);
         if (head.ch > buf.nth(head.ln).length) argErr(head);
      }
      _anch = new Position(anch.ln, anch.ch);
      _head = new Position(head.ln, head.ch);
      _cc = 0;
      if (_head.ln == _anch.ln && _head.ch == _anch.ch) return;
      if (_head.ln > _anch.ln || (_head.ln == _anch.ln && _head.ch > _anch.ch)) {
         for (int i = _anch.ln; i <= _head.ln; i++) {
            if (i == _anch.ln && i == _head.ln) _cc += _head.ch - _anch.ch;
            else if (i == _anch.ln) _cc += buf.nth(i).length - _anch.ch;
            else if (i == _head.ln) _cc += _head.ch;
            else _cc += buf.nth(i).length;
         }
         _cc += _head.ln - _anch.ln;
      }
      else {
         for (int i = _head.ln; i <= _anch.ln; i++) {
            if (i == _head.ln && i == _anch.ln) _cc -= _anch.ch - _head.ch;
            else if (i == _head.ln) _cc -= buf.nth(i).length - _head.ch;
            else if (i == _anch.ln) _cc -= _anch.ch;
            else _cc -= buf.nth(i).length;
         }
         _cc -= _anch.ln - _head.ln;
      }
      _show(width);
   }
   void kill () {
      _hide();
      _anch = null;
      _head = null;
      _cc = null;
   }

   void left (Buffer buf, int width) {
      if (_head.ln == 0 && _head.ch == 0) return;
      _hide();
      if (_head.ch == 0) {
         _head.ln--;
         _head.ch = buf.nth(_head.ln).length;
      }
      else _head.ch--;
      _cc--;
      _show(width);
   }
   void right (Buffer buf, int width) { 
      if (_head.ln == buf.lc - 1 && _head.ch == buf.nth(_head.ln).length) return;
      _hide();
      if (_head.ch == buf.nth(_head.ln).length) {
         _head.ln++;
         _head.ch = 0;
      }
      else _head.ch++;
      _cc++;
      _show(width);
   }
   void up (Buffer buf, int width, int far) {
      if (_head.ln == 0) return;
      _hide();
      _cc -= _head.ch;
      _head.ln--;
      _cc--;
      int temp = buf.nth(_head.ln).length;
      if (temp > far) _head.ch = far;
      else _head.ch = temp;
      _cc -= buf.nth(_head.ln).length - _head.ch;
      _show(width);
   }
   void down (Buffer buf, int width, int far) {
      if (_head.ln == buf.lc - 1) return;
      _hide();
      _cc += buf.nth(_head.ln).length - _head.ch;
      _head.ln++;
      _cc++;
      int temp = buf.nth(_head.ln).length;
      if (temp > far) _head.ch = far;
      else _head.ch = temp;
      _cc += _head.ch;
      _show(width);
   }
   /*
   void set anch (Position pos) {
      if (dev) {
         if (pos == null) argErr(pos);
         if (pos.ln >= _buf.lc) argErr(pos);
         if (pos.ch > _buf.nth(pos.ln).length) argErr(pos);
      }
      _anch.ln = pos.ln;
      _anch.ch = pos.ch;
      _head.ln = pos.ln;
      _head.ch = pos.ch;
      _cc = 0;
      if (dev) if (!valid) staErr(this);
   }
   */

   // observers
   Position get start {
      if (_cc >= 0) return new Position(_anch.ln, _anch.ch);
      else return new Position(_head.ln, _head.ch);
   }
   Position get end {
      if (_cc >= 0) return new Position(_head.ln, _head.ch);
      else return new Position(_anch.ln, _anch.ch);
   }
   Position get head => new Position(_head.ln, _head.ch);
   int get cc => _cc.abs();
   bool get active => _cc != null;
/*
   // invariants

   // printing
   String toString () => 'anch -> $_anch\nhead -> $_head\ncc -> $_cc';
*/
}
